class tcpbuf : public std::streambuf
{
    void initsocklib()
    {
#ifdef WIN32
        static bool inited = false;
        WSADATA wsaData;
        if (!inited) WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif
    }

   public:
    enum
    {
        BUFSIZE = 1 << 5
    };
    tcpbuf(SOCKET s)
    {
        initsocklib();
        sock = s;
    }
    tcpbuf()
    {
        initsocklib();
        sock = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    }
    ~tcpbuf() override { close(); }

    bool connect(char *ip, unsigned short port)
    {
        sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));
        addr.sin_addr.s_addr = inet_addr(ip);
        addr.sin_family = AF_INET;
        addr.sin_port = ::htons(port);
        return !::connect(sock, (sockaddr *)&addr, sizeof(addr));
    }
    int close()
    {
#ifdef WIN32
        return ::closesocket(sock);
#else
        return ::close(sock);
#endif
    }

   protected:
    // Buffered get
    int underflow() override
    {
        auto n = ::recv(sock, buf, BUFSIZE, 0);
        return n > 0 ? (setg(buf, buf, buf + n), *gptr()) : EOF;
    }
    // Unbuffered put
    int overflow(int c) override
    {
        if (c == EOF) return close();
        char b = c;
        return ::send(sock, &b, 1, 0) > 0 ? c : EOF;
    }
    std::streamsize xsputn(const char *s, std::streamsize n) override
    {
        auto x = ::send(sock, s, n, 0);
        return x > 0 ? x : 0;
    }
    // flush
    int sync() override
    {
#ifdef WIN32
        return 0;
#else
        return flush(sock);
#endif  // WIN32
    }
    // streamsize showmanyc() override { return 1; }

   private:
    SOCKET sock;
    char buf[BUFSIZE];
};

class tstream : public std::iostream
{
   public:
    tstream() : std::iostream(&_buf) {}
    tstream(SOCKET sock) : _buf(sock), std::iostream(&_buf) {}
    tstream(char *ip, unsigned short port) : tstream() { connect(ip, port); }

    bool connect(char *ip, unsigned short port) { return _buf.connect(ip, port); }

   private:
    tcpbuf _buf;
};